/******************************************************************
 * Copyright (c) 2007 - 2007, Simon Guerard, All rights reserved.
 * 
 * Projet de jeu de course au large.
 * 
 * This File is part of Sail-Race
 * 
 * Sail-Race is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 * 
 * Sail-Race is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with Sail-Race; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */
package net.simcafamily.sailrace.panel;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Paint;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.jgoodies.common.base.SystemUtils;

/**
 * A <code>JPanel</code> subclass that has a drop shadow border and that
 * provides a header with icon, title and tool bar.
 * <p>
 * 
 * This class can be used to replace the <code>JInternalFrame</code>, for use
 * outside of a <code>JDesktopPane</code>. The <code>SimpleInternalFrame</code>
 * is less flexible but often more usable; it avoids overlapping windows and
 * scales well up to IDE size. Several customers have reported that they and
 * their clients feel much better with both the appearance and the UI feel.
 * <p>
 * 
 * The SimpleInternalFrame provides the following bound properties:
 * <i>frameIcon, title, toolBar, content, selected.</i>
 * <p>
 * 
 * By default the SimpleInternalFrame is in <i>selected</i> state. If you don't
 * do anything, multiple simple internal frames will be displayed as selected.
 * 
 * @author Karsten Lentzsch
 * @version $Revision: 1.3 $
 * 
 * @see javax.swing.JInternalFrame
 * @see javax.swing.JDesktopPane
 */

public class SimpleInternalFrame extends JPanel {

    // A panel with a horizontal gradient background.
    private static class GradientPanel extends JPanel {

	/**
         * 
         */
	private static final long serialVersionUID = 599969908458421007L;

	private GradientPanel(final LayoutManager lm, final Color background) {
	    super(lm);
	    setBackground(background);
	}

	@Override
	public void paintComponent(final Graphics g) {
	    super.paintComponent(g);
	    if (!isOpaque()) {
		return;
	    }
	    final Color control = UIManager.getColor("control");
	    final int width = getWidth();
	    final int height = getHeight();

	    final Graphics2D g2 = (Graphics2D) g;
	    final Paint storedPaint = g2.getPaint();
	    g2.setPaint(new GradientPaint(0, 0, getBackground(), width, 0,
		    control));
	    g2.fillRect(0, 0, width, height);
	    g2.setPaint(storedPaint);
	}
    }

    // A custom border for the raised header pseudo 3D effect.
    private static class RaisedHeaderBorder extends AbstractBorder {

	/**
         * 
         */
	private static final long serialVersionUID = 407495689263068830L;
	private static final Insets INSETS = new Insets(1, 1, 1, 0);

	@Override
	public Insets getBorderInsets(final Component c) {
	    return RaisedHeaderBorder.INSETS;
	}

	@Override
	public void paintBorder(final Component c, final Graphics g,
		final int x, final int y, final int w, final int h) {

	    g.translate(x, y);
	    g.setColor(UIManager.getColor("controlLtHighlight"));
	    g.fillRect(0, 0, w, 1);
	    g.fillRect(0, 1, 1, h - 1);
	    g.setColor(UIManager.getColor("controlShadow"));
	    g.fillRect(0, h - 1, w, 1);
	    g.translate(-x, -y);
	}
    }

    // A custom border that has a shadow on the right and lower sides.
    private static class ShadowBorder extends AbstractBorder {

	/**
         * 
         */
	private static final long serialVersionUID = 1482149028897308934L;
	private static final Insets INSETS = new Insets(1, 1, 3, 3);

	@Override
	public Insets getBorderInsets(final Component c) {
	    return ShadowBorder.INSETS;
	}

	@Override
	public void paintBorder(final Component c, final Graphics g,
		final int x, final int y, final int w, final int h) {

	    Color shadow = UIManager.getColor("controlShadow");
	    if (shadow == null) {
		shadow = Color.GRAY;
	    }
	    final Color lightShadow = new Color(shadow.getRed(),
		    shadow.getGreen(), shadow.getBlue(), 170);
	    final Color lighterShadow = new Color(shadow.getRed(),
		    shadow.getGreen(), shadow.getBlue(), 70);
	    g.translate(x, y);

	    g.setColor(shadow);
	    g.fillRect(0, 0, w - 3, 1);
	    g.fillRect(0, 0, 1, h - 3);
	    g.fillRect(w - 3, 1, 1, h - 3);
	    g.fillRect(1, h - 3, w - 3, 1);
	    // Shadow line 1
	    g.setColor(lightShadow);
	    g.fillRect(w - 3, 0, 1, 1);
	    g.fillRect(0, h - 3, 1, 1);
	    g.fillRect(w - 2, 1, 1, h - 3);
	    g.fillRect(1, h - 2, w - 3, 1);
	    // Shadow line2
	    g.setColor(lighterShadow);
	    g.fillRect(w - 2, 0, 1, 1);
	    g.fillRect(0, h - 2, 1, 1);
	    g.fillRect(w - 2, h - 2, 1, 1);
	    g.fillRect(w - 1, 1, 1, h - 2);
	    g.fillRect(1, h - 1, w - 2, 1);
	    g.translate(-x, -y);
	}
    }

    private static final Log LOGGER = LogFactory
	    .getLog(SimpleInternalFrame.class);

    /**
     * 
     */
    private static final long serialVersionUID = -3411495895789354228L;
    private JLabel titleLabel;

    // Instance Creation ****************************************************

    private GradientPanel gradientPanel;

    private JPanel headerPanel;

    private boolean isSelected;

    /**
     * constructeur par defaut
     */
    public SimpleInternalFrame() {
	super();
    }

    // Public API ***********************************************************

    /**
     * Constructs a <code>SimpleInternalFrame</code> with the specified icon,
     * and title.
     * 
     * @param icon
     *            the initial icon
     * @param title
     *            the initial title
     */
    public SimpleInternalFrame(final Icon icon, final String title) {
	this(icon, title, null, null);
    }

    /**
     * Constructs a <code>SimpleInternalFrame</code> with the specified icon,
     * title, tool bar, and content panel.
     * 
     * @param icon
     *            the initial icon
     * @param title
     *            the initial title
     * @param bar
     *            the initial tool bar
     * @param content
     *            the initial content pane
     */
    public SimpleInternalFrame(final Icon icon, final String title,
	    final JToolBar bar, final JComponent content) {
	super(new BorderLayout());
	isSelected = false;
	titleLabel = new JLabel(title, icon, SwingConstants.LEADING);
	final JPanel top = buildHeader(titleLabel, bar);

	add(top, BorderLayout.NORTH);
	if (content != null) {
	    setContent(content);
	}
	setBorder(new ShadowBorder());
	setSelected(true);
	updateHeader();
    }

    /**
     * Constructs a <code>SimpleInternalFrame</code> with the specified title.
     * 
     * @param title
     *            the initial title
     */
    public SimpleInternalFrame(final String title) {
	this(null, title, null, null);
    }

    /**
     * Constructs a <code>SimpleInternalFrame</code> with the specified title,
     * tool bar, and content panel.
     * 
     * @param title
     *            the initial title
     * @param bar
     *            the initial tool bar
     * @param content
     *            the initial content pane
     */
    public SimpleInternalFrame(final String title, final JToolBar bar,
	    final JComponent content) {
	this(null, title, bar, content);
    }

    /**
     * Creates and answers the header panel, that consists of: an icon, a title
     * label, a tool bar, and a gradient background.
     * 
     * @param label
     *            the label to paint the icon and text
     * @param bar
     *            the panel's tool bar
     * @return the panel's built header area
     */
    private JPanel buildHeader(final JLabel label, final JToolBar bar) {
	gradientPanel = new GradientPanel(new BorderLayout(),
		getHeaderBackground());
	label.setOpaque(false);

	gradientPanel.add(label, BorderLayout.WEST);
	gradientPanel.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 1));

	headerPanel = new JPanel(new BorderLayout());
	headerPanel.add(gradientPanel, BorderLayout.CENTER);
	setToolBar(bar);
	headerPanel.setBorder(new RaisedHeaderBorder());
	headerPanel.setOpaque(false);
	return headerPanel;
    }

    /**
     * Returns the content - null, if none has been set.
     * 
     * @return the current content
     */
    public Component getContent() {
	return hasContent() ? getComponent(1) : null;
    }

    /**
     * Returns the frame's icon.
     * 
     * @return the frame's icon
     */
    public Icon getFrameIcon() {
	return titleLabel.getIcon();
    }

    /**
     * Determines and answers the header's background color. Tries to lookup a
     * special color from the L&amp;F. In case it is absent, it uses the
     * standard internal frame background.
     * 
     * @return the color of the header's background
     */
    protected Color getHeaderBackground() {
	Color c = UIManager
		.getColor("SimpleInternalFrame.activeTitleBackground");
	if (c != null) {
	    return c;
	}
	if (SystemUtils.IS_LAF_WINDOWS_XP_ENABLED) {
	    c = UIManager.getColor("InternalFrame.activeTitleGradient");
	}
	return c != null ? c : UIManager
		.getColor("InternalFrame.activeTitleBackground");
    }

    /**
     * Determines and answers the header's text foreground color. Tries to
     * lookup a special color from the L&amp;F. In case it is absent, it uses
     * the standard internal frame forground.
     * 
     * @param selected
     *            true to lookup the active color, false for the inactive
     * @return the color of the foreground text
     */
    protected Color getTextForeground(final boolean selected) {
	final Color c = UIManager
		.getColor(selected ? "SimpleInternalFrame.activeTitleForeground"
			: "SimpleInternalFrame.inactiveTitleForeground");
	if (c != null) {
	    return c;
	}
	return UIManager
		.getColor(selected ? "InternalFrame.activeTitleForeground"
			: "Label.foreground");

    }

    /**
     * Returns the frame's title text.
     * 
     * @return String the current title text
     */
    public String getTitle() {
	return titleLabel.getText();
    }

    /**
     * Returns the current toolbar, null if none has been set before.
     * 
     * @return the current toolbar - if any
     */
    public JToolBar getToolBar() {
	return headerPanel.getComponentCount() > 1 ? (JToolBar) headerPanel
		.getComponent(1) : null;
    }

    /**
     * Checks and answers if the panel has a content component set.
     * 
     * @return true if the panel has a content, false if it's empty
     */
    private boolean hasContent() {
	return getComponentCount() > 1;
    }

    // Building *************************************************************

    /**
     * Answers if the panel is currently selected (or in other words active) or
     * not. In the selected state, the header background will be rendered
     * differently.
     * 
     * @return boolean a boolean, where true means the frame is selected
     *         (currently active) and false means it is not
     */
    public boolean isSelected() {
	return isSelected;
    }

    @Override
    public void paintComponent(final Graphics g) {
	super.paintComponent(g);
	if (!isOpaque()) {
	    return;
	}
	final Color control = UIManager.getColor("control");
	final int width = getWidth();
	final int height = getHeight();

	final Graphics2D g2 = (Graphics2D) g;

	final Paint storedPaint = g2.getPaint();
	g2.setPaint(new GradientPaint(0, 0, Color.white, width, height, control));
	g2.fillRect(0, 0, width, height);
	g2.setPaint(storedPaint);
	BufferedImage image;
	try {
	    image = ImageIO.read(new File("resources/trimara_cock.jpg"));
	    RescaleOp scOp = new RescaleOp(1, 32, null);
	    g2.drawImage(scOp.filter(image, image), 0, 0, getWidth(),
		    getHeight(), this);
	} catch (IOException e) {
	    SimpleInternalFrame.LOGGER.error(e, e);
	}
    }

    /**
     * Sets a new panel content; replaces any existing content, if existing.
     * 
     * @param newContent
     *            the panel's new content
     */
    public void setContent(final Component newContent) {
	final Component oldContent = getContent();
	if (hasContent()) {
	    remove(oldContent);
	}
	add(newContent, BorderLayout.CENTER);
	firePropertyChange("content", oldContent, newContent);
    }

    // Helper Code **********************************************************

    /**
     * Sets a new frame icon.
     * 
     * @param newIcon
     *            the icon to be set
     */
    public void setFrameIcon(final Icon newIcon) {
	final Icon oldIcon = getFrameIcon();
	titleLabel.setIcon(newIcon);
	firePropertyChange("frameIcon", oldIcon, newIcon);
    }

    /**
     * This panel draws its title bar differently if it is selected, which may
     * be used to indicate to the user that this panel has the focus, or should
     * get more attention than other simple internal frames.
     * 
     * @param newValue
     *            a boolean, where true means the frame is selected (currently
     *            active) and false means it is not
     */
    public void setSelected(final boolean newValue) {
	final boolean oldValue = isSelected();
	isSelected = newValue;
	updateHeader();
	firePropertyChange("selected", oldValue, newValue);
    }

    /**
     * Sets a new title text.
     * 
     * @param newText
     *            the title text tp be set
     */
    public void setTitle(final String newText) {
	final String oldText = getTitle();
	titleLabel.setText(newText);
	firePropertyChange("title", oldText, newText);
    }

    // Helper Classes *******************************************************

    /**
     * Sets a new tool bar in the header.
     * 
     * @param newToolBar
     *            the tool bar to be set in the header
     */
    public void setToolBar(final JToolBar newToolBar) {
	final JToolBar oldToolBar = getToolBar();
	if (oldToolBar == newToolBar) {
	    return;
	}
	if (oldToolBar != null) {
	    headerPanel.remove(oldToolBar);
	}
	if (newToolBar != null) {
	    newToolBar.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
	    headerPanel.add(newToolBar, BorderLayout.EAST);
	}
	updateHeader();
	firePropertyChange("toolBar", oldToolBar, newToolBar);
    }

    /**
     * Updates the header.
     */
    private void updateHeader() {
	gradientPanel.setBackground(getHeaderBackground());
	gradientPanel.setOpaque(isSelected());
	titleLabel.setForeground(getTextForeground(isSelected()));
	headerPanel.repaint();
    }

    /**
     * Updates the UI. In addition to the superclass behavior, we need to update
     * the header component.
     */
    @Override
    public void updateUI() {
	super.updateUI();
	if (titleLabel != null) {
	    updateHeader();
	}
    }
}
